

#include <kernel/kernel.h>
#include <kernel/limits.h>
#include "handle.h"
#include <kernel/block/block.h>
#include <kernel/userid/userid.h>
#include <kernel/error/error.h>
#include <library/string/string.h>


#define HANDLE_INITIALBUFFERSIZE 0xFF
#define HANDLE_DEFAULTPERMISSIONS 0700
#define HANDLE_MAXNODES 0xFF

struct NodeEntry {
	char Byte;
	struct NodeEntry *Next;
};

struct NodeEntry NodeTable [HANDLE_MAXNODES];
struct NodeEntry *FreeList = NULL;

struct HandleEntry {
	char *Name;
	userid Owner;
	userid Group;
	int Permissions;
	int ReferenceCount;
	char Semaphore;
	struct NodeEntry *Head;
	struct NodeEntry *Tail;
	handle_Device Source;
	handle_Device Sink;
};

struct HandleEntry HandleTable [MAXHANDLES];
handle NextNewHandle = 0;

extern void handle_init ()
{
	int i;

	for (i = 0; i < MAXHANDLES; ++i)
	{
		HandleTable [i].Owner = BADUSERID;
		HandleTable [i].Name = NULL;
		HandleTable [i].Head = HandleTable [i].Tail = NULL;
	}

	for (i = 1; i < HANDLE_MAXNODES; ++i)
	{
		NodeTable [i - 1].Next = &NodeTable [i];
	}

	NodeTable [HANDLE_MAXNODES - 1].Next = NULL;
	FreeList = &NodeTable [0];
}


static struct NodeEntry *grabnode ()
{
	struct NodeEntry *p = FreeList;

	if (p == NULL)
	{
		error_log (ERROR_FULL);

		return NULL;
	}

	FreeList = p->Next;
	p->Next = NULL;

	return p;
}


static void releasenode (struct NodeEntry *_p)
{
	_p->Next = FreeList;
	FreeList = _p;
}


static void releasequeue (struct NodeEntry *_p)
{
	struct NodeEntry *q;

	for (; _p != NULL; _p = q)
	{
		q = _p->Next;
		releasenode (_p);
	}
}		


static void rawcopy (char *_dest, const char *_src, size _n)
{
	int i;

	for (i = 0; i < _n; ++i)
	{
		_dest [i] = _src [i];
	}
}


static void sync (handle _h)
{
	struct HandleEntry *hent = &HandleTable [_h];
	handle_Device temp;

	/* we disable the source/sink before calling the device function */

	if ((temp = hent->Source) != NULL)
	{
		hent->Source = NULL;

		temp (_h, hent->Name);

		hent->Source = temp;
	}

	if ((temp = hent->Sink) != NULL)
	{
		hent->Sink = NULL;

		temp (_h, hent->Name);

		hent->Sink = temp;
	}
}


static handle makehandle (const char *_name, userid _u)
{
	char giveup = 0;
	char found;
	index i;
	size sz;
	struct HandleEntry *hent;

	for (i = NextNewHandle; HandleTable [i].Owner != BADUSERID; ++i)
	{
		if (i == MAXHANDLES - 1)
		{
			if (giveup == 1)
			{
				error_log (ERROR_NOHANDLE);

				return BADHANDLE;
			}

			i = 0;
			giveup = 1;
		}
	}

	hent = &HandleTable [i];

	hent->Owner = _u;
	hent->Permissions = HANDLE_DEFAULTPERMISSIONS;
	hent->Semaphore = 1;
	hent->ReferenceCount = 1;
	hent->Head = NULL;
	hent->Tail = NULL;
	hent->Source = NULL;
	hent->Sink = NULL;

	if (_name != NULL)
	{
		sz = stringsize (_name);
		hent->Name = block_request (sz);
		rawcopy (hent->Name, _name, sz);
	}
	else
	{
		hent->Name = NULL;
	}

	return i;
}


extern handle handle_request (const char *_name, userid _u)
{
	index i;

	for (i = 0; i < MAXHANDLES; ++i)
	{
		if (HandleTable [i].Owner != BADUSERID && stringequal (HandleTable [i].Name, _name))
		{
			++HandleTable [i].ReferenceCount;
			
			return (handle) i;
		}
	}

	return makehandle (_name, _u);
}


extern void handle_release (handle _h)
{
	struct HandleEntry *hent = &HandleTable [_h];

	if (hent->Sink != NULL)
	{
		while (hent->Head != NULL)
		{
			sync (_h);
		}
	}
	else
	{
		sync (_h);
	}
	
	if (--hent->ReferenceCount > 0)
	{
		/* we weren't the only ones using this handle */

		return;
	}
	
	block_release (hent->Name);
	hent->Name = NULL;
	
	releasequeue (hent->Head);
	
	/* lastly mark it as free */
	hent->Owner = BADUSERID;
}


/* returns the number of bytes written to the queue;
 * if less than _n is written, just try again on the remainder */
extern size handle_write (handle _h, const char *_buf, size _n)
{	
	size sz;
	struct HandleEntry *hent = &HandleTable [_h];
	struct NodeEntry *p;

	if (hent->Tail == NULL)
	{
		/* Queue is empty to start */

		hent->Head = hent->Tail = grabnode ();
		hent->Tail->Byte = _buf [0];

		sz = 1;
	}
	else
	{
		sz = 0;
	}

	for (; sz < _n && (p = grabnode ()) != NULL; ++sz)
	{
		p->Byte = _buf [sz];

		hent->Tail->Next = p;
		hent->Tail = p;
	}

	sync (_h);

	return sz;
}


extern size handle_writestring (handle _h, const char *_str)
{
	size sz;
	struct HandleEntry *hent = &HandleTable [_h];
	struct NodeEntry *p;

	if (hent->Tail == NULL && _str [0] != '\0')
	{
		/* Queue is empty to start */

		hent->Head = hent->Tail = grabnode ();
		hent->Tail->Byte = _str [0];

		sz = 1;
	}
	else
	{
		sz = 0;
	}

	for (; _str [sz] != '\0' && (p = grabnode ()) != NULL; ++sz)
	{
		p->Byte = _str [sz];

		hent->Tail->Next = p;
		hent->Tail = p;
	}

	sync (_h);

	return sz;
}


extern size handle_read (handle _h, char *_buf, size _n)
{
	size sz = 0;
	struct HandleEntry *hent = &HandleTable [_h];
	struct NodeEntry *p = NULL;
	struct NodeEntry *q = NULL;

	sync (_h);

	for (p = hent->Head; sz < _n && p != NULL; p = q)
	{
		*_buf = p->Byte;
		++sz;
		++_buf;
		q = p->Next;
		releasenode (p);
	}

	if (sz > 0)
	{
		hent->Head = q;

		if (q == NULL)
		{
			hent->Tail = NULL;
		}
	}
	else
	{
		error_log (ERROR_END);
	}

	return sz;
}


extern size handle_stringlength (handle _h)
{
	size sz = 0;
	struct HandleEntry *hent = &HandleTable [_h];
	struct NodeEntry *p;

	for (p = hent->Head; p != NULL && p->Byte != '\0'; p = p->Next)
	{
		++sz;
	}

	return sz;
}


extern size handle_peak (handle _h, char *_buf, size _n)
{
	size sz = 0;
	struct HandleEntry *hent = &HandleTable [_h];
	struct NodeEntry *p;

	for (p = hent->Head; sz < _n && p != NULL; p = p->Next)
	{
		*_buf = p->Byte;
		++sz;
		++_buf;
	}

	return sz;
}


extern void handle_unlock (handle _h)
{
	++HandleTable [_h].Semaphore;
}


extern int handle_trylock (handle _h)
{
	char *s = &HandleTable [_h].Semaphore;
	int flag = -1;

	if (*s > 0)
	{
		--*s;
		flag = 0;
	}

	if (flag != 0)
	{
		error_log (ERROR_LOCKED);
	}

	return flag;
}


int handle_source (handle _h, handle_Device _source)
{
	HandleTable [_h].Source = _source;
}


int handle_sink (handle _h, handle_Device _sink)
{
	HandleTable [_h].Sink = _sink;
}



